use crate::app::error::MyError;
use crate::app::error::MyError::{BadRequest, MsgError, NotFound};
use crate::app::state::{AppState, DBS};
use crate::app::utils::{has_same_structure_with_request_body, map_myerror};
use actix_web::{web, HttpRequest, HttpResponse};
use rayon::prelude::*;
use std::sync::Arc;

pub async fn get_record(req: HttpRequest) -> Result<HttpResponse, MyError> {
    let arr_db = Arc::clone(&(DBS.get().unwrap().obj_db));
    let db = arr_db.lock().await;
    let uri = req.uri().path()[1..].to_string();
    if let Some(unwraped_db) = (*db).get(uri.clone().as_str()) {
        Ok(HttpResponse::Ok().json(unwraped_db))
    } else {
        Err(NotFound)
    }
}

pub async fn update_record(
    req: HttpRequest,
    payload: web::Payload,
    state: web::Data<AppState>,
) -> Result<HttpResponse, MyError> {
    let arr_db = Arc::clone(&(DBS.get().unwrap().obj_db));
    let mut db = arr_db.lock().await;
    let uri = req.uri().path()[1..].to_string();
    let db_key = uri.clone();
    let bytes = payload
        .to_bytes()
        .await
        .map_err(|err| MsgError(err.to_string()))?;
    let body: serde_json::Value =
        serde_json::from_slice(&bytes.to_vec()[..]).map_err(|err| MsgError(err.to_string()))?;
    let structure_map = state.get_structure_map();
    let structure = structure_map.get(db_key.clone().as_str()).unwrap();
    if has_same_structure_with_request_body(structure, &body) {
        (*db).insert(db_key, body.clone());
        Ok(HttpResponse::Ok().json(body))
    } else {
        Err(BadRequest)
    }
}

pub async fn get_list(req: HttpRequest) -> Result<HttpResponse, MyError> {
    let arr_db = Arc::clone(&(DBS.get().unwrap().arr_db));
    let db = arr_db.lock().await;
    let uri = req.uri().path()[1..].to_string();
    if let Some(unwraped_db) = (*db).get(uri.as_str()) {
        Ok(HttpResponse::Ok().json(unwraped_db))
    } else {
        Err(NotFound)
    }
}

pub async fn list_add_one(
    req: HttpRequest,
    payload: web::Payload,
    state: web::Data<AppState>,
) -> Result<HttpResponse, MyError> {
    let arr_db = Arc::clone(&(DBS.get().unwrap().arr_db));
    let mut db = arr_db.lock().await;
    let uri = req.uri().path()[1..].to_string();
    let db_key = uri.as_str();
    if let Some(unwraped_db) = (*db).get_mut(db_key) {
        let bytes = payload.to_bytes().await.map_err(map_myerror)?;
        let body: serde_json::Value =
            serde_json::from_slice(&bytes.to_vec()[..]).map_err(map_myerror)?;
        let mut deserilized_body = body.as_object().ok_or(BadRequest)?.clone();
        let structure_map = state.get_structure_map();
        let structure = structure_map.get(db_key).unwrap();
        let sample_id = structure.as_object().unwrap().get("id").unwrap();
        if sample_id.is_string() {
            let id = match unwraped_db.last() {
                Some(record) => {
                    let tmp = record
                        .as_object()
                        .ok_or(MsgError("can not parse record as object".into()))?;
                    let str_id = tmp
                        .get("id")
                        .ok_or(MsgError("can not parse record_id".into()))?
                        .as_str()
                        .ok_or(MsgError("can not parse record_id as str".into()))?;
                    let id = str_id.parse::<u64>().map_err(map_myerror)?;
                    let new_id = id + 1;
                    new_id.to_string()
                }
                None => "1".into(),
            };
            deserilized_body.insert("id".to_string(), serde_json::to_value(id).unwrap());
        } else if sample_id.is_number() {
            let id = match unwraped_db.last() {
                Some(record) => record
                    .as_object()
                    .ok_or(MsgError("can not parse record".into()))?
                    .get("id")
                    .ok_or(MsgError("can not parse record_id".into()))?
                    .as_u64()
                    .ok_or(MsgError("can not parse record_id as u64".into()))?,
                None => 1,
            };
            deserilized_body.insert("id".to_string(), serde_json::to_value(id).unwrap());
        }
        //info!("{:?}", body.to_string());
        let body = serde_json::to_value(deserilized_body).unwrap();
        if has_same_structure_with_request_body(structure, &body) {
            unwraped_db.push(body);
            Ok(HttpResponse::Ok().json(unwraped_db))
        } else {
            Err(BadRequest)
        }
    } else {
        Err(NotFound)
    }
}

pub async fn update_list(
    req: HttpRequest,
    payload: web::Payload,
    path: web::Path<(String,)>,
    state: web::Data<AppState>,
) -> Result<HttpResponse, MyError> {
    let id = path.0.clone();
    let arr_db = Arc::clone(&(DBS.get().unwrap().arr_db));
    let mut db = arr_db.lock().await;
    let uri = req.uri().to_string();
    let db_key = uri[1..].split('/').collect::<Vec<_>>()[0];
    if let Some(unwraped_db) = (*db).get_mut(db_key) {
        let arr = unwraped_db.clone();
        if let Some(idx) = arr
            .par_iter()
            .map(|it| it.as_object().unwrap())
            .position_first(|it| {
                if let Some(record_id_json) = it.get("id") {
                    let record_id = record_id_json.as_str().unwrap().to_string();
                    return record_id == id.clone();
                }
                false
            })
        {
            let bytes = payload.to_bytes().await.map_err(map_myerror)?;
            let body: serde_json::Value =
                serde_json::from_slice(&bytes.to_vec()[..]).map_err(map_myerror)?;

            let structure_map = state.get_structure_map();
            let structure = structure_map.get(db_key).unwrap();
            if has_same_structure_with_request_body(structure, &body) {
                let mut res = unwraped_db[idx]
                    .as_object()
                    .ok_or(MsgError("can not parse db value as object".into()))?
                    .clone();
                for (k, v) in body
                    .as_object()
                    .ok_or(MsgError("can not parse body as object".into()))?
                    .iter()
                {
                    if k != "id" {
                        res.insert(k.clone(), v.clone());
                    }
                }
                unwraped_db[idx] = serde_json::to_value(res).map_err(map_myerror)?;
                Ok(HttpResponse::Ok().json(unwraped_db))
            } else {
                Err(BadRequest)
            }
        } else {
            Err(NotFound)
        }
    } else {
        Err(NotFound)
    }
}

pub async fn get_by_id(
    req: HttpRequest,
    path: web::Path<(String,)>,
) -> Result<HttpResponse, MyError> {
    let id = path.0.clone();
    //info!("id: {}", id);
    let arr_db = Arc::clone(&(DBS.get().unwrap().arr_db));
    let db = arr_db.lock().await;
    let uri = req.uri().to_string();
    let db_key = uri[1..].split('/').collect::<Vec<_>>()[0];
    //info!("db_key: {}", db_key);
    //info!("{:?}", (*db).clone());
    if let Some(unwraped_db) = (*db).get(db_key) {
        let arr = unwraped_db.clone();
        //info!("arr: {:?}",arr);
        if let Some(record) = arr
            .par_iter()
            .map(|it| {
                let res = it.as_object().unwrap();
                //info!("{:?}", res);
                res
            })
            .find_first(|record| {
                //info!("record: {:?}", record);
                if let Some(record_id_json) = record.get("id") {
                    let record_id = record_id_json.as_str().unwrap().to_string();
                    //info!("record_id: {}, id: {}", record_id.as_str().unwrap().to_string(), id.clone());
                    return record_id == id.clone();
                }
                false
            })
        {
            Ok(HttpResponse::Ok().json(record))
        } else {
            Err(NotFound)
        }
    } else {
        Err(NotFound)
    }
}

pub async fn list_remove(
    req: HttpRequest,
    path: web::Path<(String,)>,
) -> Result<HttpResponse, MyError> {
    let id = path.0.clone();
    let arr_db = Arc::clone(&(DBS.get().unwrap().arr_db));
    let mut db = arr_db.lock().await;
    let uri = req.uri().to_string();
    let db_key = uri[1..].split('/').collect::<Vec<_>>()[0];
    if let Some(unwraped_db) = (*db).get_mut(db_key) {
        let arr = unwraped_db.clone();
        if let Some(idx) = arr
            .par_iter()
            .map(|it| it.as_object().unwrap())
            .position_first(|it| {
                if let Some(record_id_json) = it.get("id") {
                    let record_id = record_id_json.as_str().unwrap().to_string();
                    return record_id == id.clone();
                }
                false
            })
        {
            unwraped_db.remove(idx);
            Ok(HttpResponse::Ok().json(unwraped_db))
        } else {
            Err(NotFound)
        }
    } else {
        Err(NotFound)
    }
}
